/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package eu.anorien.dmo.proxy;

import eu.anorien.dmo.util.Compression;
import gnu.cajo.invoke.Remote;
import gnu.cajo.utils.ItemServer;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.log4j.Logger;

/**
 *
 * @author David Miguel Antunes <davidmiguel [ at ] antunes.net>
 */
public class LinuxProxy implements Proxy {

    private static final Logger logger = Logger.getLogger(LinuxProxy.class);
    private String password;
    private String MATLAB;
    private ReentrantLock runningLock = new ReentrantLock(true);
    private Process process;
    private BufferedReader in;
    private BufferedWriter out;
    private File dir;

    public LinuxProxy(String matlabPath, File dir, String password) {
        MATLAB = matlabPath;
        this.dir = dir;
        this.password = password;
        try {
            installFiles();
        } catch (IOException ex) {
            logger.error(ex);
        }
    }

    public void startServer(String host, int port) {
        try {
            Remote.config(host, port, null, 0);
            ItemServer.bind(this, Proxy.class.getSimpleName());
            logger.info(Proxy.class.getSimpleName() + " started on //" + Remote.getDefaultServerHost() + ":" + Remote.getDefaultServerPort());
        } catch (IOException ex) {
            logger.error(ex + " at " + ex.getStackTrace()[0].toString());
            throw new RuntimeException(ex);
        }

        startMatlab();
    }

    public void stopServer() {
        process.destroy();
    }

    private void startMatlab() {
        try {
            if (process != null) {
                logger.info("Killing Matlab");
                process.destroy();
            }

            String[] args = new String[]{MATLAB, "-nodisplay"};
            if (MATLAB.contains("octave")) {
                args = new String[]{MATLAB};
            }
            process = Runtime.getRuntime().exec(args, null, null);
            in = new BufferedReader(new InputStreamReader(process.getInputStream()));
            out = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));

            new Thread() {

                public void run() {
                    try {
                        BufferedReader err = new BufferedReader(new InputStreamReader(process.getErrorStream()));
                        String line;
                        while ((line = err.readLine()) != null) {
                            System.err.println(line);
                        }
                    } catch (IOException ex) {
                    }
                }
            }.start();

            String command = "";
            command += "cd " + dir.getAbsolutePath() + "\n";
            for (File file : dir.listFiles()) {
                if (file.isDirectory()) {
                    command += "addpath " + file.getName() + ";" + "\n";
                }
            }
            out.write(command);
            out.flush();
        } catch (IOException ex) {
            logger.fatal(ex);
        }
    }

    public void ping() {
    }

    public static void main(String[] args) throws UnknownHostException, FileNotFoundException, IOException {
        //BasicConfigurator.configure();

        if (args.length == 0) {
            new LinuxProxyConfig().setVisible(true);
        } else {
            String matlabPath = "/usr/bin/matlab";
            String hostname = InetAddress.getLocalHost().getHostName();
            int port = 1198;
            String password = "";
            if (args.length > 0) {
                matlabPath = args[0];
            }
            if (args.length > 1) {
                hostname = args[1];
            }
            if (args.length > 2) {
                port = Integer.parseInt(args[2]);
            }
            if (args.length > 3) {
                password = (args[3]);
            }
            LinuxProxy server = new LinuxProxy(matlabPath, new File("./"), password);
            server.startServer(hostname, port);

            while (true) {
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException ex) {
                }
            }
        }
    }

    private void installFiles() throws FileNotFoundException, IOException {
        String[] files = new String[]{ //            "linux_uncompress_folder.sh",
        //            "linux_matlab_run_function.sh",
        };
        for (int i = 0; i < files.length; i++) {
            String file = files[i];
            FileOutputStream fos = new FileOutputStream(file);
            InputStream is = getClass().getClassLoader().getResourceAsStream(file);
            while (is.available() > 0) {
                fos.write(is.read());
            }
            is.close();
            fos.close();
            new File(file).setExecutable(true);
        }
    }

    @Override
    public double runFunction(String pass, String name, String parameters) throws ServerBusyException {
        boolean success = runningLock.tryLock();
        if (!success) {
            throw new ServerBusyException("Server is already handling one request");
        } else {
            try {
                if (password != null && !password.equals("") && !password.equals(pass)) {
                    throw new RuntimeException("Wrong password.");
                }
                
                logger.info("Running function " + name + " with params " + parameters);
                double performance = -1;
                try {
                    String command = "";
                    command += "xxxxerrorRatexxxx=-1;" + "\n";
                    command += "[xxxxerrorRatexxxx] = " + name + "(" + parameters + ");" + "\n";
                    if (MATLAB.contains("octave")) {
                        command += "printf('xxxxerrorRatexxxx=%f\\n', xxxxerrorRatexxxx);" + "\n";
                    } else {
                        command += "display(sprintf('xxxxerrorRatexxxx=%f\\n', xxxxerrorRatexxxx));" + "\n";
                    }

                    out.write(command);
                    out.flush();

                    String line;
                    while (!(line = in.readLine()).contains("xxxxerrorRatexxxx="));
                    try {
                        performance = Double.parseDouble(line.split("=")[1]);
                    } catch (Exception e) {
                    }
                } catch (IOException ex) {
                    logger.error(ex);
                    startMatlab();
                }
                return performance;
            } finally {
                runningLock.unlock();
            }
        }
    }

    @Override
    public void installFolder(
            String pass,
            String name,
            byte[] tarFile) {
        if (!pass.equals(password)) {
            throw new RuntimeException("Wrong password.");
        }
        runningLock.lock();
        logger.info("Installing folder " + name);
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(dir.getAbsoluteFile() + "/" + name + ".zip");
            fos.write(tarFile, 0, tarFile.length);
            fos.close();

            Compression.uncompressZip(new File(dir.getAbsoluteFile() + "/" + name + ".zip"), dir);

            startMatlab();
//            new File("folder.zip").delete();
        } catch (IOException ex) {
            logger.error(ex);
        } finally {
            runningLock.unlock();
        }
    }
}
